﻿//    TestGenerator - experiment with piping data into ffmpeg in "pipefmt"
//    Copyright (C) 2012  Juergen Bohl

//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.

//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.

//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

namespace TestGenerator
{
    #region Using directives
    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.IO;
    using System.Text;
    #endregion

    /// <summary>
    /// This class encapsulates the (external) ffmpeg process. It is utilized to start
    /// the process.
    /// </summary>
    public class FfmpegProcess : IDisposable
    {
        private FfmpegProcessOptions processOptions;

        private Process process;

        /// <summary>
        /// Initializes a new instance of the <see cref="FfmpegProcess"/> class.
        /// </summary>
        /// <param name="processOptions">The options for launching the ffmpeg process.</param>
        public FfmpegProcess(FfmpegProcessOptions processOptions)
        {
            this.processOptions = processOptions;
        }

        /// <summary>
        /// Finalizes an instance of the <see cref="FfmpegProcess"/> class.
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="FfmpegProcess"/> is reclaimed by garbage collection.
        /// </summary>
        ~FfmpegProcess()
        {
            this.Dispose(false);
        }

        /// <summary>
        /// Starts the ffmpeg-process.
        /// </summary>
        public void Start()
        {
            string cmdLine = this.BuildCmdLine();

            ProcessStartInfo startInfo = new ProcessStartInfo
                {
                    FileName = this.processOptions.FfmpegFileName,
                    Arguments = cmdLine,
                    UseShellExecute = false,
                    CreateNoWindow = false/*true*/         /*  specify false in order to see a console with ffmpeg */
                };

            Process ffmpegProcess = new Process { StartInfo = startInfo };

            bool startSucceeded = false;

            try
            {
                startSucceeded = ffmpegProcess.Start();
            }
            catch (InvalidOperationException)
            {
            }
            catch (Win32Exception)
            {
            }

            if (startSucceeded == false)
            {
                throw new FileNotFoundException(string.Format("Could not launch the application \"{0}\".", this.processOptions.FfmpegFileName));
            }

            this.process = ffmpegProcess;
        }

        public void Close()
        {
            this.Dispose();
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources.
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Release managed resources:
                // Call Dispose() on other objects owned by this instance.
                if (this.process != null)
                {
                    this.process.Dispose();
                }
            }
        }

        private string BuildCmdLine()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat(
                "-loglevel debug {2} -f pipefmt -i winfile:{0} -target pal-dv \"{1}\"",
                this.processOptions.PipeName,
                this.processOptions.DestinationFileName,
                this.processOptions.OverWriteDestinationFile ? "-y" : string.Empty);
            return sb.ToString();
        }

        /// <summary>
        /// The options for launching the ffmpeg process.
        /// </summary>
        public struct FfmpegProcessOptions
        {
            private string ffmpegFileName;

            private string pipeName;

            private string destinationFileName;

            private bool overWrite;

            /// <summary>
            /// Initializes a new instance of the <see cref="FfmpegProcessOptions"/> struct.
            /// </summary>
            /// <param name="ffmpegFileName">The fully qualified name of the ffmpeg executable.</param>
            /// <param name="pipeName">The fully qualified name of the pipe.</param>
            /// <param name="destinationFileName">Name of the destination file.</param>
            public FfmpegProcessOptions(string ffmpegFileName, string pipeName, string destinationFileName, bool overWrite)
            {
                this.ffmpegFileName = ffmpegFileName;
                this.pipeName = pipeName;
                this.destinationFileName = destinationFileName;
                this.overWrite = overWrite;
            }

            /// <summary>
            /// Gets a value indicating whether this instance has valid data.
            /// </summary>
            /// <value>
            /// <c>True</c> if this instance has valid data; otherwise, <c>false</c>.
            /// </value>
            public bool IsValid
            {
                get
                {
                    if (string.IsNullOrWhiteSpace(this.ffmpegFileName) ||
                        string.IsNullOrWhiteSpace(this.pipeName) ||
                        string.IsNullOrWhiteSpace(this.destinationFileName))
                    {
                        return false;
                    }

                    return true;
                }
            }

            /// <summary>
            /// Gets the fully qualified name of the ffmpeg executable.
            /// </summary>
            /// <value>
            /// The fully qualified name of the ffmpeg executable.
            /// </value>
            public string FfmpegFileName
            {
                get { return this.ffmpegFileName; }
            }

            /// <summary>
            /// Gets the fully qualified name of the named pipe.
            /// </summary>
            /// <value>
            /// The fully qualified name of the named pipe.
            /// </value>
            public string PipeName
            {
                get { return this.pipeName; }
            }

            /// <summary>
            /// Gets the name of the destination file.
            /// </summary>
            /// <value>
            /// The name of the destination file.
            /// </value>
            public string DestinationFileName
            {
                get { return this.destinationFileName; }
            }

            public bool OverWriteDestinationFile
            {
                get { return this.overWrite; }
            }
        }
    }
}
